package com.orangeforms.webadmin.upms.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ReflectUtil;
import com.orangeforms.common.core.annotation.MyRequestBody;
import com.orangeforms.common.core.constant.ErrorCodeEnum;
import com.orangeforms.common.core.object.ResponseResult;
import com.orangeforms.common.core.upload.BaseUpDownloader;
import com.orangeforms.common.core.upload.UpDownloaderFactory;
import com.orangeforms.common.core.upload.UploadResponseInfo;
import com.orangeforms.common.core.upload.UploadStoreInfo;
import com.orangeforms.common.core.util.MyCommonUtil;
import com.orangeforms.common.core.util.MyModelUtil;
import com.orangeforms.common.log.annotation.OperationLog;
import com.orangeforms.common.log.model.constant.SysOperationLogType;
import com.orangeforms.common.redis.cache.SessionCacheHelper;
import com.orangeforms.webadmin.config.ApplicationConfig;
import com.orangeforms.webadmin.upms.dto.AttachResourceDto;
import com.orangeforms.webadmin.upms.dto.VisibleResourceDto;
import com.orangeforms.webadmin.upms.model.AttachResource;
import com.orangeforms.webadmin.upms.model.BusinessCollection;
import com.orangeforms.webadmin.upms.model.VisibleResource;
import com.orangeforms.webadmin.upms.model.SysUser;
import com.orangeforms.webadmin.upms.service.BusinessCollectionService;
import com.orangeforms.webadmin.upms.service.BusinessDigitalResourceService;
import com.orangeforms.webadmin.upms.service.SysUserService;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author：howlrou
 * @Date：2024/8/7 9:51
 */

@Tag(name = "数字资源管理接口")
@Slf4j
@RestController
@RequestMapping("/admin/business/digitalResource")
public class DigitalResourceController {

    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SessionCacheHelper cacheHelper;

    @Autowired
    private UpDownloaderFactory upDownloaderFactory;

    @Autowired
    private ApplicationConfig appConfig;

    @Autowired
    private BusinessDigitalResourceService digitalResourceService;

    @Autowired
    private BusinessCollectionService collectionService;


    /**
     * 获取可预览数字资源
     */
    @SaCheckPermission("digitalResource.view")
    @GetMapping(value = "visibleResource/{collectionId}")
    public ResponseResult<List<VisibleResourceDto>> getVisibleResource(@PathVariable("collectionId") Long collectionId) {
        VisibleResource visibleResourceQuery = new VisibleResource();
        visibleResourceQuery.setCollectionId(collectionId);
        List<VisibleResource> visibleResources = digitalResourceService.selectVisibleResourceList(visibleResourceQuery);
        ArrayList<VisibleResourceDto> visibleResourceDtos = new ArrayList<>();
        if (null != visibleResources && !visibleResources.isEmpty()) {
            for (VisibleResource visibleResource : visibleResources) {
                VisibleResourceDto visibleResourceDto = MyModelUtil.copyTo(visibleResource, VisibleResourceDto.class);
                visibleResourceDto.setFilePathUrl(appConfig.getStaticUrl() + visibleResource.getFilePath());
                visibleResourceDtos.add(visibleResourceDto);

            }
        }
        return ResponseResult.success(visibleResourceDtos);
    }

    /**
     * 获取附件数字资源
     */
    @SaCheckPermission("digitalResource.view")
    @GetMapping(value = "attachResource/{collectionId}")
    public ResponseResult<List<AttachResourceDto>> getAttachResource(@PathVariable("collectionId") Long collectionId) {
        AttachResource attachResourceQuery = new AttachResource();
        attachResourceQuery.setCollectionId(collectionId);
        List<AttachResource> attachResources = digitalResourceService.selectAttachResourceList(attachResourceQuery);
        ArrayList<AttachResourceDto> attachResourceDtos = new ArrayList<>();
        if (null != attachResources && !attachResources.isEmpty()) {
            for (AttachResource attachResource : attachResources) {
                AttachResourceDto attachResourceDto = MyModelUtil.copyTo(attachResource, AttachResourceDto.class);
                attachResourceDto.setFilePathUrl(appConfig.getStaticUrl() + attachResource.getFilePath());
                attachResourceDtos.add(attachResourceDto);
            }
        }

        return ResponseResult.success(attachResourceDtos);
    }



    /**
     * 修改可预览文件名称
     */
    @SaCheckPermission("digitalResource.update")
    @OperationLog(type = SysOperationLogType.UPDATE)
    @PostMapping("/update/visibleName")
    public ResponseResult<Void> updateVisibleName(@MyRequestBody VisibleResource visibleResource) {

        VisibleResource originalVisibleResource = digitalResourceService.getById(visibleResource.getDigitalResourceId());
        if (originalVisibleResource == null) {
            return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
        }
        BusinessCollection businessCollection = collectionService.getById(originalVisibleResource.getCollectionId());
        VisibleResource visibleResourceUpdate = new VisibleResource();
        visibleResourceUpdate.setDigitalResourceId(visibleResource.getDigitalResourceId());
        visibleResourceUpdate.setFileName(visibleResource.getFileName() + "." + originalVisibleResource.getResourceType());
        String updatePath = BaseUpDownloader.makeUpdateResourceSavePath(true,
                businessCollection.getEnterId(),
                visibleResourceUpdate.getFileName());
        visibleResourceUpdate.setFilePath(updatePath);
        digitalResourceService.updateVisibleResource(visibleResourceUpdate);

        digitalResourceService.updateAttachResource(appConfig.getUploadFileBaseDir() + "/" + originalVisibleResource.getFilePath(),
                appConfig.getUploadFileBaseDir() + "/" + updatePath);
        return ResponseResult.success();
    }

    /**
     * 修改附件资源文件名称
     */
    @SaCheckPermission("digitalResource.update")
    @OperationLog(type = SysOperationLogType.UPDATE)
    @PostMapping("/update/attachName")
    public ResponseResult<Void> updateAttachName(@MyRequestBody AttachResource attachResource) {

        AttachResource existAttachResource = digitalResourceService.selectAttachResourceByDigitalResourceId(attachResource.getDigitalResourceId());
        if (existAttachResource == null) {
            return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
        }
        BusinessCollection collection = collectionService.getById(existAttachResource.getCollectionId());
        AttachResource attachResourceUpdate = new AttachResource();
        attachResourceUpdate.setDigitalResourceId(attachResource.getDigitalResourceId());
        attachResourceUpdate.setFileName(attachResource.getFileName() + "." + existAttachResource.getResourceType());

        String updatePath = BaseUpDownloader.makeUpdateResourceSavePath(false, collection.getEnterId(),
                attachResourceUpdate.getFileName());
        attachResourceUpdate.setFilePath(updatePath);
        attachResourceUpdate.setFilePath(updatePath);
        digitalResourceService.updateAttachResource(attachResourceUpdate);

        digitalResourceService.updateAttachResource(appConfig.getUploadFileBaseDir() + "/" + existAttachResource.getFilePath(),
                appConfig.getUploadFileBaseDir() + "/" + updatePath);
        return ResponseResult.success();
    }


    /**
     * 文件下载。
     * 这里将图片和其他类型的附件文件放到不同的父目录下，主要为了便于今后图片文件的迁移。
     *
     * @param userId    附件所在记录的主键Id。
     * @param filename  文件名。如果没有提供该参数，就从当前记录的指定字段中读取。
     * @param asVisible 下载文件是否为图片。
     * @param response  Http 应答对象。
     */
    @SaCheckPermission("digitalResource.download")
    @OperationLog(type = SysOperationLogType.DOWNLOAD, saveResponse = false)
    @GetMapping("/download")
    public void download(
            @RequestParam(required = false) Long userId,
            @RequestParam String filename,
            @RequestParam Boolean asVisible,
            HttpServletResponse response) {
        if (MyCommonUtil.existBlankArgument("filePath", filename, asVisible)) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        // 使用try来捕获异常，是为了保证一旦出现异常可以返回500的错误状态，便于调试。
        // 否则有可能给前端返回的是200的错误码。
        try {
            // 如果请求参数中没有包含主键Id，就判断该文件是否为当前session上传的。
            if (userId == null) {
                if (!cacheHelper.existSessionUploadFile(filename)) {
                    ResponseResult.output(HttpServletResponse.SC_FORBIDDEN);
                    return;
                }
            } else {
                SysUser sysUser = sysUserService.getById(userId);
                if (sysUser == null) {
                    ResponseResult.output(HttpServletResponse.SC_NOT_FOUND);
                    return;
                }
                String fieldJsonData = (String) ReflectUtil.getFieldValue(sysUser, "filePath");
                if (fieldJsonData == null && !cacheHelper.existSessionUploadFile(filename)) {
                    ResponseResult.output(HttpServletResponse.SC_BAD_REQUEST);
                    return;
                }
                if (!BaseUpDownloader.containFile(fieldJsonData, filename)
                        && !cacheHelper.existSessionUploadFile(filename)) {
                    ResponseResult.output(HttpServletResponse.SC_FORBIDDEN);
                    return;
                }
            }
            UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(VisibleResource.class, "filePath");
            if (!storeInfo.isSupportUpload()) {
                ResponseResult.output(HttpServletResponse.SC_NOT_IMPLEMENTED,
                        ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FIELD));
                return;
            }
            BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType());
            upDownloader.doDownload(appConfig.getUploadFileBaseDir(),
                    VisibleResource.class.getSimpleName(), "filePath", filename, asVisible, response);
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            log.error(e.getMessage(), e);
        }
    }


    /**
     * 藏品封面上传接口。
     *
     * @param uploadFile 上传文件对象。
     */
    @SaCheckPermission("sysUser.view")
    @OperationLog(type = SysOperationLogType.UPLOAD, saveResponse = false)
    @PostMapping("/uploadCover")
    public void uploadCover(
            @RequestParam("uploadFile") MultipartFile uploadFile) throws IOException {
        UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(BusinessCollection.class, "cover");
        // 这里就会判断参数中指定的字段，是否支持上传操作。
        if (!storeInfo.isSupportUpload()) {
            ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
                    ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FIELD));
            return;
        }
        // 根据字段注解中的存储类型，通过工厂方法获取匹配的上传下载实现类，从而解耦。
        BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType());
        UploadResponseInfo responseInfo = upDownloader.coverUpload(null,
                appConfig.getUploadFileBaseDir(), BusinessCollection.class.getSimpleName(), "cover", true, true,uploadFile);
        if (Boolean.TRUE.equals(responseInfo.getUploadFailed())) {
            ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
                    ResponseResult.error(ErrorCodeEnum.UPLOAD_FAILED, responseInfo.getErrorMessage()));
            return;
        }
        cacheHelper.putSessionUploadFile(responseInfo.getFilename());
        ResponseResult.output(ResponseResult.success(responseInfo));
    }

    /**
     * 文件上传操作。
     *
     * @param visible    是否可以在系统中预览，可预览和不可预览资源分不同目录存储
     * @param uploadFile 上传文件对象。
     */
    @SaCheckPermission("digitalResource.upload")
    @OperationLog(type = SysOperationLogType.UPLOAD, saveResponse = false)
    @PostMapping("/upload")
    public void upload(
            @RequestParam Long collectionId,
            @RequestParam Boolean visible,
            @RequestParam("uploadFile") MultipartFile uploadFile) throws IOException {
        UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(VisibleResource.class, "filePath");
        // 这里就会判断参数中指定的字段，是否支持上传操作。
        if (!storeInfo.isSupportUpload()) {
            ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
                    ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FIELD));
            return;
        }
        BusinessCollection existCollection = collectionService.getById(collectionId);
        if (null == existCollection) {
            ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
                    ResponseResult.error(ErrorCodeEnum.COLLECTION_NOT_EXIST));
            return;
        }
        // 根据字段注解中的存储类型，通过工厂方法获取匹配的上传下载实现类，从而解耦。
        BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType());
        UploadResponseInfo responseInfo = upDownloader.resourceUpload(null,
                appConfig.getUploadFileBaseDir(), existCollection.getEnterId(), visible, uploadFile);
        if (Boolean.TRUE.equals(responseInfo.getUploadFailed())) {
            ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
                    ResponseResult.error(ErrorCodeEnum.UPLOAD_FAILED, responseInfo.getErrorMessage()));
            return;
        }
        responseInfo.setSqlSave(responseInfo.getSqlSave() + "/" + responseInfo.getFilename());
        if (visible) {
            VisibleResource visibleResource = new VisibleResource();
            visibleResource.setCollectionId(collectionId);
            visibleResource.setFileName(responseInfo.getFilename());
            visibleResource.setFilePath(responseInfo.getSqlSave());
            visibleResource.setResourceType(FilenameUtils.getExtension(uploadFile.getOriginalFilename()));
            digitalResourceService.insertVisibleResource(visibleResource);
        } else {
            AttachResource attachResource = new AttachResource();
            attachResource.setCollectionId(collectionId);
            attachResource.setFileName(responseInfo.getFilename());
            attachResource.setFilePath(responseInfo.getSqlSave());
            attachResource.setResourceType(FilenameUtils.getExtension(uploadFile.getOriginalFilename()));
            digitalResourceService.insertAttachResource(attachResource);
        }

        cacheHelper.putSessionUploadFile(responseInfo.getFilename());
        ResponseResult.output(ResponseResult.success(responseInfo));
    }

    /**
     * 可预览资源删除
     *
     * @param visibleResources 包含了删除路径
     */
    @SaCheckPermission("digitalResource.deleteResource")
    @OperationLog(type = SysOperationLogType.DELETE, saveResponse = false)
    @PostMapping("/deleteVisible")
    public ResponseResult<Void> deleteVisible(
            @MyRequestBody List<VisibleResource> visibleResources) throws IOException {
        if (MyCommonUtil.existBlankArgument(visibleResources)) {
            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
        }

        for (VisibleResource visibleResource : visibleResources) {
            digitalResourceService.
                    deleteVisibleFile(visibleResource.getDigitalResourceId(), visibleResource.getFilePath());
        }

        return ResponseResult.success();
    }

    /**
     * 附件删除
     *
     * @param attachResources 待删除附件对象。
     * @return 应答结果对象。
     */
    @SaCheckPermission("digitalResource.delete")
    @OperationLog(type = SysOperationLogType.DELETE)
    @PostMapping("/deleteAttach")
    public ResponseResult<Void> deleteAttach(@MyRequestBody List<AttachResource> attachResources) {
        if (MyCommonUtil.existBlankArgument(attachResources)) {
            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
        }

        for (AttachResource attachResource : attachResources) {
            digitalResourceService.
                    deleteAttachFile(attachResource.getDigitalResourceId(), attachResource.getFilePath());
        }
        return ResponseResult.success();
    }


    /**
     * 扫描目录将文件写入sql
     *
     * @param collectionIds 待扫描藏品id。
     * @return 应答结果对象。
     */
    @SaCheckPermission("digitalResource.scan")
    @OperationLog(type = SysOperationLogType.DELETE)
    @PostMapping("/scanResource")
    public ResponseResult<Void> scanResource(@MyRequestBody List<Long> collectionIds) {
        if (null == collectionIds || collectionIds.isEmpty()) {
            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
        }
        digitalResourceService.scanResource(collectionIds);

        return ResponseResult.success();
    }
}
